﻿//  --------------------------------
//  Copyright (c) AW Systems, Inc. All rights reserved.
//  This source code is made available under the terms of the Microsoft Public License (Ms-PL)
//  http://www.codeplex.com/SLAM/license
//  ---------------------------------

using System;
using AWS.SLAM.Configuration;
using AWS.SLAM.Data;
using AWS.SLAM.Events;
using AWS.SLAM.Hierarchy.Data;
using AWS.SLAM.Logging;
using AWS.SLAM.SharePoint;
using Microsoft.SharePoint;
using System.Text.RegularExpressions;

namespace AWS.SLAM.Hierarchy
{
	/// <summary>
	/// HierarchicalContentTypeItemUpdater updates an item to SLAM's datasource(s) when a new List Item 
	/// configured to be managed by SLAM according to its Content Type is updated in SharePoint then
	/// updates its position in the hierarchy of items also managed by SLAM.  
	/// 
	/// A Content Type in SharePoint configured to use the HierarchicalContentTypeItemUpdater requires 
	/// that it contain a field of type HierarchyPositionField.
	/// </summary>
	public class HierarchicalContentTypeItemUpdater : ContentTypeItemUpdater
	{
		#region Private State

		/// <summary>
		/// SLAM Data service layer
		/// </summary>
		private IDataServices _dataServices;

		/// <summary>
		/// SLAM Configuration
		/// </summary>
		private IConfigurationManager _configuration;

		/// <summary>
		/// SLAM Logger
		/// </summary>
		private ILogger _logger;

		#endregion

		#region Constructors

		/// <summary>
		/// Creates instance of HierarchicalContentTypeItemUpdater for the given 
		/// DataServices, Configuration, and Logger.
		/// </summary>
		/// <param name="dataServices"></param>
		/// <param name="configuration"></param>
		/// <param name="logger"></param>
		public HierarchicalContentTypeItemUpdater(IDataServices dataServices, IConfigurationManager configuration, ILogger logger)
			: base(dataServices, configuration, logger)
		{
			_dataServices = dataServices;
			_configuration = configuration;
			_logger = logger;
		}

		#endregion

		/// <summary>
		/// Updates an item in SLAM's external datasource then uses the value of the HierarchyPositionField
		/// that must be present in the given listItem to insert into or move the item in the hierarchy
		/// managed by SLAM for the List Item's Content Type.
		/// </summary>
		/// <param name="listItem"></param>
		/// <returns></returns>
		public override bool Update(SPListItem listItem)
		{
			//Execute default SLAM Update process for a configured Content Type
			bool updateResult = base.Update(listItem);

			string typeName = SlamItemContext.TypeName;

			//Open Hierarchy's DataService with the Default ConnectionString and DataSchema.
			//This process assumes the Hierarchy is saved in a SQL Server database and is using
			//a SLAM XML Configuration file.
			DataService dataService = new DataService(_configuration.GetSection<ConnectionStringSection>("ConnectionStrings").Default,
				((XmlSqlDataMapping)_configuration.DataMapping).GetDataSchema());

			//Check configuration for the presence of a TableName for the listItem's
			//ContentType.  If it is present, use it as the item's typeName instead of the
			//ContentType Name.  Otherwise if it is not present use the ContentType's Name
			string itemTypeName = _configuration.DataMapping.GetTypeAttribute("", typeName, "TableName");
			if (String.IsNullOrEmpty(itemTypeName))
			{
				itemTypeName = typeName;
			}

			TypeIdDefinition typeIdDefinition = new TypeIdDefinition();
			string typeIdDefinitionName = _configuration.DataMapping.GetTypeAttribute("", typeName, "TypeIdDefinition");
			if (String.IsNullOrEmpty(typeIdDefinitionName))
			{
				typeIdDefinitionName = ((XmlSqlDataMapping)_configuration.DataMapping).GetTypeIdDefinitionName();
			}
			TypeIdDefinitionSection typeIdDefininitionSection = _configuration.GetSection<TypeIdDefinitionSection>("TypeIdDefinitions");

			if (String.IsNullOrEmpty(typeIdDefinitionName))
			{
				typeIdDefinition = typeIdDefininitionSection.Default;
			}
			else
			{
				typeIdDefinition = typeIdDefininitionSection.GetTypeIdDefinition(typeIdDefinitionName);
			}

			string idColumnName = Regex.Replace(String.IsNullOrEmpty(typeIdDefinition.NameFormat) ? "ID" : typeIdDefinition.NameFormat.Replace("{TypeName}", _configuration.DataMapping.GetTypeAttribute("", typeName, "Name")).Replace("{TableName}", itemTypeName), "[\\W\\s]", "");
			string idColumnType = typeIdDefinition.SqlType;

			//For this type, add the Hierarchy "Left", "Right", and "Hierarchy Level" columns
			//in the SLAM table if they do not exist.
			dataService.AddHierarchyColumns(itemTypeName);

			//Get the Hierarchy Position Field's value from the listItem regardless of its name
			string hierarchyPositionFieldValue = listItem.GetHierarchyPositionFieldValue();

			if (!String.IsNullOrEmpty(hierarchyPositionFieldValue))
			{
				//The Hierarchy Position Field's value has 3 parts, the first
				//part contains a Parent GUID Id, the second part contains a sibling
				//ListItem GUID Id, the third indicates whether to add, "a", or move, "m", the item
				string[] fieldValueParts = hierarchyPositionFieldValue.Split('|');
				if (fieldValueParts.Length > 2 && !String.IsNullOrEmpty(fieldValueParts[0]))
				{
					string id = Convert.ToString(listItem.GetUniqueId());
					if (fieldValueParts[2] == "a")
					{
						//If the first part is an Empty Guid then this listItem is a root node. Otherwise
						//add as a child node
						if (fieldValueParts[0] != Guid.Empty.ToString())
						{
							//If the second part is an Empty Guid then this listItem goes at the end
							//of the parent's children, otherwise it goes to the left, in place, of the
							//sibling defined by fieldValuesParts[1]
							if (!String.IsNullOrEmpty(fieldValueParts[1]) && fieldValueParts[1] != Guid.Empty.ToString())
							{
								dataService.AddAsLeftSiblingNode(itemTypeName, idColumnName, fieldValueParts[1], id);
							}
							else
							{
								dataService.AddAsLastChildNode(itemTypeName, idColumnName, fieldValueParts[0], id);
							}
						}
						else
						{
							dataService.AddAsRootNode(itemTypeName, idColumnName, id);
						}
					}
					else if (fieldValueParts[2] == "m")
					{
						//If the first part is an Empty Guid then this listItem should become a root node. 
						//Otherwise it should become a child node
						if (fieldValueParts[0] != Guid.Empty.ToString())
						{
							//If the second part is an Empty Guid then this listItem goes at the end
							//of the parent's children, otherwise it goes to the left, in place, of the
							//sibling defined by fieldValuesParts[1]
							if (!String.IsNullOrEmpty(fieldValueParts[1]) && fieldValueParts[1] != Guid.Empty.ToString())
							{
								if (dataService.MoveToLeftSiblingNode(itemTypeName, idColumnName, idColumnType, fieldValueParts[1], id) < 1)
								{
									dataService.AddAsLeftSiblingNode(itemTypeName, idColumnName, fieldValueParts[1], id);
								}
							}
							else if (dataService.MoveToLastChildNode(itemTypeName, idColumnName, idColumnType, fieldValueParts[0], id) < 1)
							{
								dataService.AddAsLastChildNode(itemTypeName, idColumnName, fieldValueParts[0], id);
							}
						}
						else
						{
							if (dataService.MoveToRootNode(itemTypeName, idColumnName, idColumnType, id) < 1)
							{
								dataService.AddAsRootNode(itemTypeName, idColumnName, id);
							}
						}
					}

					dataService.RecalculateHierarchyLevels(itemTypeName, idColumnName);
				}
			}

			return updateResult;
		}
	}
}
